function type(data) {
    let type = null
    if (
        typeof data == "string" ||
        typeof data == "number" ||
        typeof data == "boolean" ||
        typeof data == "undefined" ||
        typeof data == "function" ||
        typeof data == 'symbol'
    ) {
        type = typeof data
        return type
    } else if (typeof data == "object") {
        if (data instanceof Date) {
            type = 'date'
        } else if (data instanceof Set) {
            type = 'set'
        } else if (data instanceof Map) {
            type = 'map'
        } else if (Array.isArray(data)) {
            type = 'array'
        } else {
            type = 'object'
            return type
        }
        return type
    }
}

function deepCopy(obj) {
    if (
        ['string', 'number', 'boolean', 'function', 'undefined', 'symbol']
            .includes(type(obj))) {
        return obj;
    }
    let arr = "";
    if (type(obj) === 'array') {
        arr = [];
        arr = obj.map((item) => {
            return deepCopy(item);
        });
    } else if (type(obj) === "object") {
        arr = {};
        for (let key in obj) {
            if (Object.prototype.hasOwnProperty.call(obj, key)) {
                arr[key] = deepCopy(obj[key]);
            }
        }
    }
    return arr;
}

function depthMerger(oldData, newData) {
    oldData = deepCopy(oldData)
    let onetype = type(oldData), onetypes = type(newData)
    if (onetype === onetypes) {
        if (onetype === 'array' || onetype === 'object') {
            for (let key in newData) {
                if (Object.prototype.hasOwnProperty.call(newData, key)) {
                    oldData[key] = depthMerger(oldData[key], newData[key])
                }
            }
        } else if (onetype === 'date' || onetype === 'set' || onetype === 'map') {
            oldData = deepCopy(newData)
        } else {
            oldData = newData
        }
    } else {
        oldData = deepCopy(newData)
    }
    return oldData;
}

function depthMergers(callback) {
    let args,callbackState = false;
    if(type(callback) === 'function'){
        callbackState = true
        args = Array.prototype.slice.call(arguments,1)
    }else{
        args = Array.from(arguments)
    }

    return args.reduce((previousValue, currentValue,currentIndex,array) => {
        if(callbackState===true){
            currentValue = callback(previousValue, currentValue,currentIndex,array)
        }
        return depthMerger(previousValue, currentValue)
    }, {})
}

export {
    type,
    deepCopy,
    depthMerger,
    depthMergers
}